package com.cherrish.demo.nio;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Iterator;
import java.util.Set;
import java.util.concurrent.CountDownLatch;

import static java.nio.charset.StandardCharsets.UTF_8;

/**
 * @author cherrish
 * @time 2019-03-01 10:37
 * @name EchoClient
 * @desc:
 */
public class EchoClient {

    public static void main(String[] args) throws IOException, InterruptedException {
        EchoClient client = new EchoClient();
        client.startAioClient();
    }
    public void startAioClient() throws InterruptedException, IOException {
        CountDownLatch latch = new CountDownLatch(1);
        AsynchronousSocketChannel channel = AsynchronousSocketChannel.open();
        channel.connect(new InetSocketAddress("localhost",9090), channel, new ConnectCompletionHandler(latch));
        latch.await();
    }

    static class ConnectCompletionHandler implements CompletionHandler<Void, AsynchronousByteChannel>{

        private CountDownLatch latch;

        public ConnectCompletionHandler(CountDownLatch latch) {
            this.latch = latch;
        }

        @Override
        public void completed(Void result, AsynchronousByteChannel channel) {
            ByteBuffer buffer = ByteBuffer.wrap("Hello, I'm client.".getBytes());
            channel.write(buffer, buffer, new CompletionHandler<Integer, ByteBuffer>() {
                @Override
                public void completed(Integer result, ByteBuffer buffer) {
                    if(buffer.hasRemaining()){
                        channel.write(buffer, buffer, this);
                    }else {
                        ByteBuffer readBuffer = ByteBuffer.allocate(1024);
                        channel.read(readBuffer, readBuffer, new CompletionHandler<Integer, ByteBuffer>() {
                            @Override
                            public void completed(Integer result, ByteBuffer attachment) {
                                attachment.flip();
                                byte[] bytes = new byte[attachment.remaining()];
                                readBuffer.get(bytes);
                                try {
                                    System.out.println("Client receives message: ");
                                    System.out.println(new String(bytes, UTF_8));
                                    latch.countDown();
                                } catch (Exception e) {
                                    e.printStackTrace();
                                }
                            }

                            @Override
                            public void failed(Throwable exc, ByteBuffer attachment) {
                                try {
                                    channel.close();
                                    latch.countDown();
                                } catch (IOException e) {
                                    e.printStackTrace();
                                }
                            }
                        });
                    }
                }

                @Override
                public void failed(Throwable exc, ByteBuffer attachment) {
                    try {
                        channel.close();
                        latch.countDown();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            });
        }

        @Override
        public void failed(Throwable exc, AsynchronousByteChannel attachment) {

        }
    }

    public void startNioClient() throws IOException {
        SocketChannel client = SocketChannel.open();
        client.configureBlocking(false);
        Selector selector = Selector.open();
        client.register(selector, SelectionKey.OP_CONNECT);
        client.connect(new InetSocketAddress("localhost", 9090));
        while (true){
            selector.select();
            Set<SelectionKey> keys = selector.selectedKeys();
            Iterator<SelectionKey> iterator = keys.iterator();
            while (iterator.hasNext()){
                SelectionKey key = iterator.next();
                iterator.remove();

                if(key.isConnectable()){
                    connect(key, selector);
                }else if(key.isReadable()){
                    read(key);
                }else if(key.isWritable()){
                    write(key, selector);
                }

            }

        }
    }

    private void connect(SelectionKey key, Selector selector) throws IOException {
        SocketChannel channel = (SocketChannel) key.channel();
        channel.register(selector, SelectionKey.OP_WRITE);
        channel.finishConnect();
    }

    private void write(SelectionKey key, Selector selector) throws IOException {
        SocketChannel channel = (SocketChannel) key.channel();
        request(channel);
        channel.register(selector, SelectionKey.OP_READ);
    }

    private void read(SelectionKey key) throws IOException {
        SocketChannel channel = (SocketChannel) key.channel();
        handleResponse(channel);
        channel.close();
    }

    public void startClient() throws IOException {
        SocketChannel client = SocketChannel.open();
        client.connect(new InetSocketAddress("localhost", 9090));
        request(client);
        handleResponse(client);

    }

    private void request(SocketChannel channel) throws IOException {
        ByteBuffer buffer = ByteBuffer.wrap(("Hello, I'm client.").getBytes());
        channel.write(buffer);
    }
    private void handleResponse(SocketChannel channel) throws IOException {
        ByteBuffer buffer = ByteBuffer.allocate(1024);
        channel.read(buffer);
        buffer.flip();
        byte[] bytes = new byte[buffer.remaining()];
        buffer.get(bytes);
        System.out.println("Client receives message: ");
        System.out.println(new String(bytes, UTF_8));
    }
}
